from rest_framework import serializers
from goods.models import SKU, SKUSpecification
from goods.models import GoodsCategory, SPU
from goods.models import SPUSpecification, SpecificationOption
from django.db import transaction
import logging

logger = logging.getLogger('django')


class SKUSpecificationSerializers(serializers.ModelSerializer):
    """ SKU具体规格 模型类序列化器 """

    spec_id = serializers.IntegerField()
    option_id = serializers.IntegerField()

    class Meta:
        # SKUSpecification中sku外键关联了SKU表
        model = SKUSpecification
        # 隐藏情况：主键隐藏字段，和外键隐藏字段默认映射的时候read_only=True只参与序列化
        fields = ["spec_id", "option_id"]


class SKUSerializer(serializers.ModelSerializer):
    """ 商品SKU 模型类序列化器"""

    # 1、外键关联对象字段序列化结果为名称，类型重写定义为StringRelatedField
    # 2、外键关联对象的主键隐藏字段不会自动映射，所以我们需要自定义
    spu = serializers.StringRelatedField(read_only=True)
    spu_id = serializers.IntegerField()
    category = serializers.StringRelatedField(read_only=True)
    category_id = serializers.IntegerField()
    # 3、关联从表隐藏字段 --> 多个SKUSpecification模型类对象(由主到从嵌套序列化)
    specs = SKUSpecificationSerializers(many=True)

    class Meta:
        # 注：SKU表中category外键关联了GoodsCategory分类表。spu外键关联了SPU商品表
        model = SKU
        # fields = "__all__"    # __all__表示显示定义字段和主键隐藏字段会自动映射
        exclude = ["create_time", "update_time", "default_image"]

    def create(self, validated_data):
        # TODO：因为模型类序列化器默认的create方法，无法根据specs字段新建中间表数据
        # 1、把specs字段从有效数据中pop出来，specs = [{spec_id: "4", option_id: 8}, {spec_id: "5", option_id: 11}]
        specs = validated_data.pop("specs")

        # 1.1 由于涉及到两表的新建操作，使用事务进行操作
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                # 2、新建sku对象
                sku_model = SKU.objects.create(**validated_data)
                # 3、新建中间表数据SKUSpecification
                for spec in specs:                  # spec = {'spec_id': 4, 'option_id': 8}
                    spec["sku_id"] = sku_model.id   # spec = {'spec_id': 4, 'option_id': 8, 'sku_id': 17}
                    SKUSpecification.objects.create(**spec)
            except Exception as e:
                logger.error(e)
                transaction.savepoint_rollback(save_id)
            transaction.savepoint_commit(save_id)

        return sku_model

    def update(self, instance, validated_data):
        # TODO：因为模型类序列化器默认的update方法，无法根据specs字段来更新中间表数据
        # 1、从有效数据中剔除specs，specs = [{spec_id: "4", option_id: 10}, {spec_id: "5", option_id: 12}]
        specs = validated_data.pop("specs")

        # 1.1 由于涉及到两表的新建操作，使用事务进行操作
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                # 2、更新sku数据
                sku_model = super().update(instance, validated_data)
                # 3、根据新的规格选项插入中间表数据SKUSpecification
                # 3.1、清除原有的当前sku的规格和选项
                SKUSpecification.objects.filter(sku=sku_model).delete()
                # 3.2、根据新的规格选项插入中间表数据
                for spec in specs:
                    spec["sku_id"] = sku_model.id
                    SKUSpecification.objects.create(**spec)
            except Exception as e:
                logger.error(e)
                transaction.savepoint_rollback(save_id)
            transaction.savepoint_commit(save_id)

        return sku_model


class GoodsCategorySerializer(serializers.ModelSerializer):
    """ 商品类别 模型类系列化器 """

    class Meta:
        model = GoodsCategory
        fields = ["id", "name"]


class SPUSimpleSerializer(serializers.ModelSerializer):
    """ 商品SPU 模型类序列化器 """

    class Meta:
        model = SPU
        fields = ["id", "name"]


class SpecificationOptionSerializer(serializers.ModelSerializer):
    """ SPU规格选项 模型类序列化器 """

    class Meta:
        model = SpecificationOption
        fields = ["id", "value"]


class SPUSpecificationSerializer(serializers.ModelSerializer):
    """ SPU规格 模型类序列化器 """

    spu = serializers.StringRelatedField(read_only=True)
    spu_id = serializers.IntegerField()
    # 关联序列化返回 规格选项信息
    options = SpecificationOptionSerializer(read_only=True, many=True)

    class Meta:
        model = SPUSpecification
        fields = ["id", "name", "spu", "spu_id", "options"]
